package com.kexilo.config.service;

import com.kexilo.config.domain.ConfigData;
import com.kexilo.config.properties.ConfigProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.scheduling.annotation.Async;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

/**
 * 配置服务默认实现
 * 支持数据库、缓存、异步、配置变更监听等多种处理方式
 * 
 * @author Kexilo
 */
public class ConfigServiceImpl implements ConfigService {
    
    private static final Logger log = LoggerFactory.getLogger(ConfigServiceImpl.class);
    
    private final ConfigProperties properties;
    private final JdbcTemplate jdbcTemplate;
    private final CacheManager cacheManager;
    
    // 缓存统计
    private final Map<String, Long> hitCount = new ConcurrentHashMap<>();
    private final Map<String, Long> missCount = new ConcurrentHashMap<>();
    
    // 配置变更监听器
    private final Map<String, List<ConfigChangeListener>> changeListeners = new ConcurrentHashMap<>();
    
    public ConfigServiceImpl(ConfigProperties properties, 
                          JdbcTemplate jdbcTemplate, 
                          CacheManager cacheManager) {
        this.properties = properties;
        this.jdbcTemplate = jdbcTemplate;
        this.cacheManager = cacheManager;
        log.info("配置服务初始化完成，缓存策略: {}, 数据源类型: {}", 
                properties.getCacheStrategy(), properties.getDataSourceType());
    }
    
    @Override
    public String getConfigValue(String configKey) {
        return getConfigValue(configKey, null);
    }
    
    @Override
    public String getConfigValue(String configKey, String defaultValue) {
        if (!StringUtils.hasText(configKey)) {
            return defaultValue;
        }
        
        long startTime = System.currentTimeMillis();
        
        try {
            // 尝试从缓存获取
            String cachedValue = getFromCache(configKey);
            if (cachedValue != null) {
                incrementHitCount(configKey);
                return "NULL_VALUE".equals(cachedValue) ? defaultValue : cachedValue;
            }
            
            incrementMissCount(configKey);
            
            // 从数据库查询
            ConfigData configData = queryFromDatabase(configKey);
            String configValue = configData != null ? configData.getConfigValue() : null;
            
            // 存入缓存（包括空值）
            putToCache(configKey, configValue != null ? configValue : "NULL_VALUE");
            
            long costTime = System.currentTimeMillis() - startTime;
            log.debug("查询配置值: {}, 耗时: {}ms, 值: {}", configKey, costTime, configValue);
            
            // 慢查询监控
            if (properties.getMonitor().isEnabled() && 
                costTime > properties.getMonitor().getSlowQueryThreshold().toMillis()) {
                log.warn("配置查询慢查询告警: {}, 耗时: {}ms", configKey, costTime);
            }
            
            return configValue != null ? configValue : defaultValue;
            
        } catch (Exception e) {
            log.error("查询配置值失败: {}", configKey, e);
            return defaultValue;
        }
    }
    
    @Override
    @Async("configTaskExecutor")
    public CompletableFuture<String> getConfigValueAsync(String configKey) {
        try {
            String result = getConfigValue(configKey);
            return CompletableFuture.completedFuture(result);
        } catch (Exception e) {
            log.error("异步查询配置值失败: {}", configKey, e);
            return CompletableFuture.failedFuture(e);
        }
    }
    
    @Override
    public ConfigData getConfigData(String configKey) {
        if (!StringUtils.hasText(configKey)) {
            return null;
        }
        
        try {
            return queryFromDatabase(configKey);
        } catch (Exception e) {
            log.error("查询配置数据失败: {}", configKey, e);
            return null;
        }
    }
    
    @Override
    public Map<String, String> getConfigValues(List<String> configKeys) {
        if (configKeys == null || configKeys.isEmpty()) {
            return Collections.emptyMap();
        }
        
        Map<String, String> result = new HashMap<>();
        
        if (properties.getDatabase().isBatchQueryEnabled() && configKeys.size() > 1) {
            // 批量查询优化
            try {
                Map<String, ConfigData> batchResult = batchQueryFromDatabase(configKeys);
                for (String key : configKeys) {
                    ConfigData configData = batchResult.get(key);
                    result.put(key, configData != null ? configData.getConfigValue() : null);
                }
            } catch (Exception e) {
                log.error("批量查询配置值失败", e);
                // 降级到单个查询
                for (String configKey : configKeys) {
                    result.put(configKey, getConfigValue(configKey));
                }
            }
        } else {
            // 单个查询
            for (String configKey : configKeys) {
                result.put(configKey, getConfigValue(configKey));
            }
        }
        
        return result;
    }
    
    @Override
    public List<ConfigData> getAllConfigs() {
        try {
            String sql = "SELECT config_id, config_name, config_key, config_value, config_type, " +
                        "remark, create_by, create_time, update_by, update_time " +
                        "FROM " + properties.getDatabase().getConfigTable() + 
                        " ORDER BY config_id ASC";
            return jdbcTemplate.query(sql, getConfigDataRowMapper());
        } catch (Exception e) {
            log.error("查询所有配置失败", e);
            return Collections.emptyList();
        }
    }
    
    @Override
    public List<ConfigData> getConfigsByType(String configType) {
        try {
            String sql = "SELECT config_id, config_name, config_key, config_value, config_type, " +
                        "remark, create_by, create_time, update_by, update_time " +
                        "FROM " + properties.getDatabase().getConfigTable() + 
                        " WHERE config_type = ? ORDER BY config_id ASC";
            return jdbcTemplate.query(sql, getConfigDataRowMapper(), configType);
        } catch (Exception e) {
            log.error("根据类型查询配置失败: {}", configType, e);
            return Collections.emptyList();
        }
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveOrUpdateConfig(ConfigData configData) {
        if (configData == null || !StringUtils.hasText(configData.getConfigKey())) {
            return false;
        }
        
        try {
            String oldValue = getConfigValue(configData.getConfigKey());
            
            if (exists(configData.getConfigKey())) {
                // 更新
                String updateSql = "UPDATE " + properties.getDatabase().getConfigTable() + 
                                  " SET config_name = ?, config_value = ?, config_type = ?, " +
                                  "remark = ?, update_by = ?, update_time = ? WHERE config_key = ?";
                int result = jdbcTemplate.update(updateSql,
                        configData.getConfigName(),
                        configData.getConfigValue(),
                        configData.getConfigType(),
                        configData.getRemark(),
                        configData.getUpdateBy(),
                        LocalDateTime.now(),
                        configData.getConfigKey());
                
                if (result > 0) {
                    // 清除缓存
                    evictFromCache(configData.getConfigKey());
                    // 触发变更监听
                    notifyListeners(configData.getConfigKey(), oldValue, configData.getConfigValue());
                    return true;
                }
            } else {
                // 新增
                String insertSql = "INSERT INTO " + properties.getDatabase().getConfigTable() + 
                                 " (config_name, config_key, config_value, config_type, remark, " +
                                 "create_by, create_time) VALUES (?, ?, ?, ?, ?, ?, ?)";
                int result = jdbcTemplate.update(insertSql,
                        configData.getConfigName(),
                        configData.getConfigKey(),
                        configData.getConfigValue(),
                        configData.getConfigType(),
                        configData.getRemark(),
                        configData.getCreateBy(),
                        LocalDateTime.now());
                
                if (result > 0) {
                    // 触发变更监听
                    notifyListeners(configData.getConfigKey(), oldValue, configData.getConfigValue());
                    return true;
                }
            }
            
        } catch (Exception e) {
            log.error("保存配置失败: {}", configData.getConfigKey(), e);
            throw e;
        }
        
        return false;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteConfig(String configKey) {
        if (!StringUtils.hasText(configKey)) {
            return false;
        }
        
        try {
            String oldValue = getConfigValue(configKey);
            String deleteSql = "DELETE FROM " + properties.getDatabase().getConfigTable() + 
                              " WHERE config_key = ?";
            int result = jdbcTemplate.update(deleteSql, configKey);
            
            if (result > 0) {
                // 清除缓存
                evictFromCache(configKey);
                // 触发变更监听
                notifyListeners(configKey, oldValue, null);
                return true;
            }
            
        } catch (Exception e) {
            log.error("删除配置失败: {}", configKey, e);
            throw e;
        }
        
        return false;
    }
    
    @Override
    public boolean exists(String configKey) {
        return getConfigData(configKey) != null;
    }
    
    @Override
    public void refreshCache(String configKey) {
        try {
            if (StringUtils.hasText(configKey)) {
                evictFromCache(configKey);
                getConfigValue(configKey); // 重新加载
                log.info("刷新配置缓存: {}", configKey);
            } else {
                clearAllCache();
                warmupCache(null);
                log.info("刷新所有配置缓存");
            }
        } catch (Exception e) {
            log.error("刷新配置缓存失败: {}", configKey, e);
        }
    }
    
    @Override
    public void warmupCache(List<String> configKeys) {
        if (!properties.isWarmupEnabled()) {
            return;
        }
        
        try {
            List<String> keysToWarmup = configKeys;
            if (keysToWarmup == null || keysToWarmup.isEmpty()) {
                // 使用配置的预热键
                keysToWarmup = properties.getWarmupKeys();
                if (keysToWarmup.isEmpty()) {
                    // 预热所有配置
                    keysToWarmup = getAllConfigs().stream()
                                                  .map(ConfigData::getConfigKey)
                                                  .collect(Collectors.toList());
                }
            }
            
            log.info("开始预热配置缓存，键数量: {}", keysToWarmup.size());
            
            for (String configKey : keysToWarmup) {
                try {
                    getConfigValue(configKey);
                    log.debug("预热配置缓存: {}", configKey);
                } catch (Exception e) {
                    log.warn("预热配置缓存失败: {}", configKey, e);
                }
            }
            
            log.info("配置缓存预热完成");
            
        } catch (Exception e) {
            log.error("配置缓存预热失败", e);
        }
    }
    
    @Override
    public void clearAllCache() {
        try {
            if (cacheManager != null) {
                Cache cache = cacheManager.getCache(properties.getCache().getCacheName());
                if (cache != null) {
                    cache.clear();
                }
            }
            hitCount.clear();
            missCount.clear();
            log.info("清除所有配置缓存");
        } catch (Exception e) {
            log.error("清除配置缓存失败", e);
        }
    }
    
    @Override
    public boolean getBooleanValue(String configKey) {
        return getBooleanValue(configKey, false);
    }
    
    @Override
    public boolean getBooleanValue(String configKey, boolean defaultValue) {
        String value = getConfigValue(configKey);
        if (value == null) {
            return defaultValue;
        }
        return "true".equalsIgnoreCase(value) || 
               "yes".equalsIgnoreCase(value) || 
               "1".equals(value) ||
               "Y".equalsIgnoreCase(value);
    }
    
    @Override
    public Integer getIntegerValue(String configKey) {
        return getIntegerValue(configKey, null);
    }
    
    @Override
    public Integer getIntegerValue(String configKey, Integer defaultValue) {
        String value = getConfigValue(configKey);
        if (value == null) {
            return defaultValue;
        }
        try {
            return Integer.parseInt(value);
        } catch (NumberFormatException e) {
            log.warn("配置值格式错误，无法转换为整数: {} = {}", configKey, value);
            return defaultValue;
        }
    }
    
    @Override
    public Long getLongValue(String configKey) {
        return getLongValue(configKey, null);
    }
    
    @Override
    public Long getLongValue(String configKey, Long defaultValue) {
        String value = getConfigValue(configKey);
        if (value == null) {
            return defaultValue;
        }
        try {
            return Long.parseLong(value);
        } catch (NumberFormatException e) {
            log.warn("配置值格式错误，无法转换为长整数: {} = {}", configKey, value);
            return defaultValue;
        }
    }
    
    @Override
    public Double getDoubleValue(String configKey) {
        return getDoubleValue(configKey, null);
    }
    
    @Override
    public Double getDoubleValue(String configKey, Double defaultValue) {
        String value = getConfigValue(configKey);
        if (value == null) {
            return defaultValue;
        }
        try {
            return Double.parseDouble(value);
        } catch (NumberFormatException e) {
            log.warn("配置值格式错误，无法转换为双精度数: {} = {}", configKey, value);
            return defaultValue;
        }
    }
    
    @Override
    public void addConfigChangeListener(String configKey, ConfigChangeListener listener) {
        if (!properties.isChangeListenerEnabled() || listener == null) {
            return;
        }
        
        changeListeners.computeIfAbsent(configKey, k -> new CopyOnWriteArrayList<>()).add(listener);
        log.debug("添加配置变更监听器: {}", configKey);
    }
    
    @Override
    public void removeConfigChangeListener(String configKey, ConfigChangeListener listener) {
        if (!properties.isChangeListenerEnabled() || listener == null) {
            return;
        }
        
        List<ConfigChangeListener> listeners = changeListeners.get(configKey);
        if (listeners != null) {
            listeners.remove(listener);
            if (listeners.isEmpty()) {
                changeListeners.remove(configKey);
            }
        }
        log.debug("移除配置变更监听器: {}", configKey);
    }
    
    @Override
    public boolean isAvailable() {
        try {
            if (jdbcTemplate != null) {
                jdbcTemplate.queryForObject("SELECT 1", Integer.class);
                return true;
            }
            return false;
        } catch (Exception e) {
            log.warn("配置服务不可用: {}", e.getMessage());
            return false;
        }
    }
    
    @Override
    public Map<String, Object> getCacheStats() {
        Map<String, Object> stats = new HashMap<>();
        
        long totalHits = hitCount.values().stream().mapToLong(Long::longValue).sum();
        long totalMisses = missCount.values().stream().mapToLong(Long::longValue).sum();
        long totalRequests = totalHits + totalMisses;
        
        stats.put("totalHits", totalHits);
        stats.put("totalMisses", totalMisses);
        stats.put("totalRequests", totalRequests);
        stats.put("hitRate", totalRequests > 0 ? (double) totalHits / totalRequests : 0.0);
        stats.put("hitCountByKey", new HashMap<>(hitCount));
        stats.put("missCountByKey", new HashMap<>(missCount));
        stats.put("listenersCount", changeListeners.size());
        
        return stats;
    }
    
    /**
     * 从数据库查询配置数据
     */
    private ConfigData queryFromDatabase(String configKey) {
        String sql = "SELECT config_id, config_name, config_key, config_value, config_type, " +
                    "remark, create_by, create_time, update_by, update_time " +
                    "FROM " + properties.getDatabase().getConfigTable() + 
                    " WHERE config_key = ?";
        
        List<ConfigData> results = jdbcTemplate.query(sql, getConfigDataRowMapper(), configKey);
        return results.isEmpty() ? null : results.get(0);
    }
    
    /**
     * 批量查询配置数据
     */
    private Map<String, ConfigData> batchQueryFromDatabase(List<String> configKeys) {
        String inClause = configKeys.stream()
                .map(key -> "?")
                .collect(Collectors.joining(","));
        
        String sql = "SELECT config_id, config_name, config_key, config_value, config_type, " +
                    "remark, create_by, create_time, update_by, update_time " +
                    "FROM " + properties.getDatabase().getConfigTable() + 
                    " WHERE config_key IN (" + inClause + ")";
        
        List<ConfigData> allData = jdbcTemplate.query(sql, getConfigDataRowMapper(), configKeys.toArray());
        
        return allData.stream()
                .collect(Collectors.toMap(ConfigData::getConfigKey, data -> data));
    }
    
    /**
     * ConfigData行映射器
     */
    private RowMapper<ConfigData> getConfigDataRowMapper() {
        return (rs, rowNum) -> ConfigData.builder()
                .configId(rs.getLong("config_id"))
                .configName(rs.getString("config_name"))
                .configKey(rs.getString("config_key"))
                .configValue(rs.getString("config_value"))
                .configType(rs.getString("config_type"))
                .remark(rs.getString("remark"))
                .createBy(rs.getString("create_by"))
                .createTime(rs.getTimestamp("create_time") != null ? 
                           rs.getTimestamp("create_time").toLocalDateTime() : null)
                .updateBy(rs.getString("update_by"))
                .updateTime(rs.getTimestamp("update_time") != null ? 
                           rs.getTimestamp("update_time").toLocalDateTime() : null)
                .build();
    }
    
    /**
     * 从缓存获取数据
     */
    private String getFromCache(String configKey) {
        if (properties.getCacheStrategy() == ConfigProperties.CacheStrategy.NONE || cacheManager == null) {
            return null;
        }
        
        try {
            Cache cache = cacheManager.getCache(properties.getCache().getCacheName());
            if (cache != null) {
                String cacheKey = properties.getCache().getKeyPrefix() + configKey;
                Cache.ValueWrapper wrapper = cache.get(cacheKey);
                if (wrapper != null) {
                    return (String) wrapper.get();
                }
            }
        } catch (Exception e) {
            log.warn("从缓存获取数据失败: {}", configKey, e);
        }
        
        return null;
    }
    
    /**
     * 存入缓存
     */
    private void putToCache(String configKey, String configValue) {
        if (properties.getCacheStrategy() == ConfigProperties.CacheStrategy.NONE || cacheManager == null) {
            return;
        }
        
        try {
            Cache cache = cacheManager.getCache(properties.getCache().getCacheName());
            if (cache != null) {
                String cacheKey = properties.getCache().getKeyPrefix() + configKey;
                cache.put(cacheKey, configValue);
            }
        } catch (Exception e) {
            log.warn("存入缓存失败: {}", configKey, e);
        }
    }
    
    /**
     * 从缓存中移除
     */
    private void evictFromCache(String configKey) {
        if (cacheManager == null) {
            return;
        }
        
        try {
            Cache cache = cacheManager.getCache(properties.getCache().getCacheName());
            if (cache != null) {
                String cacheKey = properties.getCache().getKeyPrefix() + configKey;
                cache.evict(cacheKey);
            }
        } catch (Exception e) {
            log.warn("从缓存移除失败: {}", configKey, e);
        }
    }
    
    /**
     * 通知配置变更
     */
    private void notifyListeners(String configKey, String oldValue, String newValue) {
        if (!properties.isChangeListenerEnabled()) {
            return;
        }
        
        List<ConfigChangeListener> listeners = changeListeners.get(configKey);
        if (listeners != null && !listeners.isEmpty()) {
            for (ConfigChangeListener listener : listeners) {
                try {
                    listener.onConfigChanged(configKey, oldValue, newValue);
                } catch (Exception e) {
                    log.error("配置变更监听器执行失败: {}", configKey, e);
                }
            }
        }
    }
    
    /**
     * 增加命中计数
     */
    private void incrementHitCount(String configKey) {
        hitCount.merge(configKey, 1L, Long::sum);
    }
    
    /**
     * 增加未命中计数
     */
    private void incrementMissCount(String configKey) {
        missCount.merge(configKey, 1L, Long::sum);
    }
}
